// Copyright 2020, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';

/// github地址：https://github.com/badjio/skeletons
class DxSkeletonTheme extends InheritedWidget {
  final LinearGradient? shimmerGradient;
  final LinearGradient? darkShimmerGradient;
  final ThemeMode? themeMode;

  const DxSkeletonTheme({
    Key? key,
    required Widget child,
    this.shimmerGradient,
    this.darkShimmerGradient,
    this.themeMode,
  }) : super(key: key, child: child);

  static DxSkeletonTheme? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<DxSkeletonTheme>();

  @override
  bool updateShouldNotify(DxSkeletonTheme oldWidget) =>
      oldWidget.themeMode != themeMode ||
      oldWidget.shimmerGradient != shimmerGradient ||
      oldWidget.darkShimmerGradient != darkShimmerGradient;
}

class DxShimmerWidget extends StatefulWidget {
  const DxShimmerWidget({
    Key? key,
    this.shimmerGradient,
    this.darkShimmerGradient,
    this.themeMode,
    this.duration,
    this.child,
  }) : super(key: key);

  final LinearGradient? shimmerGradient;
  final LinearGradient? darkShimmerGradient;
  final ThemeMode? themeMode;
  final Duration? duration;
  final Widget? child;

  @override
  State<DxShimmerWidget> createState() => DxShimmerState();
}

class DxShimmerState extends State<DxShimmerWidget> with SingleTickerProviderStateMixin {
  late AnimationController _shimmerController;

  LinearGradient shimmerGradient = const LinearGradient(
    colors: [Color(0xFFEBEBF4), Color(0xFFD6D6E4), Color(0xFFD1D1DF), Color(0xFFD6D6E4), Color(0xFFEBEBF4)],
    stops: [0.0, 0.3, 0.5, 0.7, 1],
    begin: Alignment(-2.4, -0.2),
    end: Alignment(2.4, 0.2),
    tileMode: TileMode.clamp,
  );

  LinearGradient darkShimmerGradient = const LinearGradient(
    colors: [Color(0xFF222222), Color(0xFF242424), Color(0xFF2B2B2B), Color(0xFF242424), Color(0xFF222222)],
    stops: [0.0, 0.2, 0.5, 0.8, 1],
    begin: Alignment(-2.4, -0.2),
    end: Alignment(2.4, 0.2),
    tileMode: TileMode.clamp,
  );

  @override
  void initState() {
    super.initState();
    // WidgetsBinding.instance?.addPostFrameCallback((_) => _setGradient());
    _shimmerController = AnimationController.unbounded(vsync: this)
      ..repeat(min: -2.0, max: 2, period: widget.duration ?? const Duration(milliseconds: 1000));
  }

  @override
  void dispose() {
    _shimmerController.dispose();
    super.dispose();
  }

  ThemeMode get _appThemeMode => Theme.of(context).brightness == Brightness.dark ? ThemeMode.dark : ThemeMode.light;

  @visibleForTesting
  LinearGradient get gradient =>
      ((widget.themeMode ?? DxSkeletonTheme.of(context)?.themeMode ?? _appThemeMode) == ThemeMode.dark)
          ? _darkGradient
          : _lightGradient;

  LinearGradient get _lightGradient =>
      widget.shimmerGradient ?? DxSkeletonTheme.of(context)?.shimmerGradient ?? shimmerGradient;

  LinearGradient get _darkGradient =>
      widget.darkShimmerGradient ?? DxSkeletonTheme.of(context)?.darkShimmerGradient ?? darkShimmerGradient;

  LinearGradient get currentGradient => LinearGradient(
        colors: gradient.colors,
        stops: gradient.stops,
        begin: gradient.begin,
        end: gradient.end,
        transform: _SlidingGradientTransform(slidePercent: _shimmerController.value),
      );

  bool get isSized => context.findRenderObject() != null ? (context.findRenderObject() as RenderBox).hasSize : false;

  Size get size => (context.findRenderObject() as RenderBox).size;

  Offset getDescendantOffset({
    required RenderBox descendant,
    Offset offset = Offset.zero,
  }) {
    final shimmerBox = context.findRenderObject() as RenderBox;
    return descendant.localToGlobal(offset, ancestor: shimmerBox);
  }

  Listenable get shimmerChanges => _shimmerController;

  @override
  Widget build(BuildContext context) {
    return widget.child != null ? DxShimmer(shimmer: this, child: widget.child!) : const SizedBox.shrink();
  }
}

class _SlidingGradientTransform extends GradientTransform {
  const _SlidingGradientTransform({
    required this.slidePercent,
  });

  final double slidePercent;

  @override
  Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
    return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0);
  }
}

class DxShimmer extends InheritedWidget {
  final DxShimmerState shimmer;
  const DxShimmer({
    Key? key,
    required Widget child,
    required this.shimmer,
  }) : super(key: key, child: child);

  static DxShimmerState? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<DxShimmer>()?.shimmer;

  @override
  bool updateShouldNotify(DxShimmer oldWidget) => shimmer != oldWidget.shimmer;
}
